#pragma once

#include"IEventManager.h"
#include<map>
#include<list>

class IEventListener;

#define g_PG_pInputEventManager g_PG_pPaleGemFramework->GetInputEventManager()

/**
Used to determine a class when registering an event.
*/
enum EKeyPressEventClass
{
	eKPEC_press,
	eKPEC_release,
};

/**
PaleGem internal key references.
32 input mappings are supperted at the moment.
*/
enum EKeyPressKeyEventName : unsigned int
{
	PG_KEY1 = 1,
	PG_KEY2 = 1 << 1,
	PG_KEY3 = 1 << 2,
	PG_KEY4 = 1 << 3,
	PG_KEY5 = 1 << 4,
	PG_KEY6 = 1 << 5,
	PG_KEY7 = 1 << 6,
	PG_KEY8 = 1 << 7,
	PG_KEY9 = 1 << 8,
	PG_KEY10 = 1 << 9,
	PG_KEY11 = 1 << 10,
	PG_KEY12 = 1 << 11,
	PG_KEY13 = 1 << 12,
	PG_KEY14 = 1 << 13,
	PG_KEY15 = 1 << 14,
	PG_KEY16 = 1 << 15,
	PG_KEY17 = 1 << 16,
	PG_KEY18 = 1 << 17,
	PG_KEY19 = 1 << 18,
	PG_KEY20 = 1 << 19,
	PG_KEY21 = 1 << 20,
	PG_KEY22 = 1 << 21,
	PG_KEY23 = 1 << 22,
	PG_KEY24 = 1 << 23,
	PG_KEY25 = 1 << 24,
	PG_KEY26 = 1 << 25,
	PG_KEY27 = 1 << 26,
	PG_KEY28 = 1 << 27,
	PG_KEY29 = 1 << 28,
	PG_KEY30 = 1 << 29,
	PG_KEY31 = 1 << 30,
	PG_KEY32 = 1 << 31,
};

/**
Windows virtual key maps for 0 - 9 and A - Z.
*/
enum EWindowsVirtualKeys
{
	VK_0 = 0x30,
	VK_1 = 0x31,
	VK_2 = 0x32,
	VK_3 = 0x33,
	VK_4 = 0x34,
	VK_5 = 0x35,
	VK_6 = 0x36,
	VK_7 = 0x37,
	VK_8 = 0x38,
	VK_9 = 0x39,

	VK_A = 0x41,
	VK_B = 0x42,
	VK_C = 0x43,
	VK_D = 0x44,
	VK_E = 0x45,
	VK_F = 0x46,
	VK_G = 0x47,
	VK_H = 0x48,
	VK_I = 0x49,
	VK_J = 0x4A,
	VK_K = 0x4B,
	VK_L = 0x4C,
	VK_M = 0x4D,
	VK_N = 0x4E,
	VK_O = 0x4F,
	VK_P = 0x50,
	VK_Q = 0x51,
	VK_R = 0x52,
	VK_S = 0x53,
	VK_T = 0x54,
	VK_U = 0x55,
	VK_V = 0x56,
	VK_W = 0x57,
	VK_X = 0x58,
	VK_Y = 0x59,
	VK_Z = 0x5A,
};

/**
PaleGem's keyboard event manager.
Processed keyboard inputs.

Inititalizing
-------------
First you have to map a windows key to a palegem key. This is done by calling MapKey().
The virtual key is a windows key (See also EWindowsVirtualKeys) while pgKey is one of the 32 palegem keys.
Note that you can map multiple windows keys to the same palegem key but not vice versa.
After mapping the desired keys you will have to take care of sending the WM_KEYDOWN and WM_KEYUP events from your window procedure
to the input event manager, by calling RegisterEvent() on both messages. Use wParam for the event name,
eKPEC_press or eKPEC_release for the eventclass during the respective message, and NULL for event data (note that event data is not processed at all,
meaning you _can_ send some sort of data to the event manager but it will simply ignore it and do nothing with it, including not sending it to listeners).
A good idea is to create your own enum mapping for the palegem keys, e.g.

enum MyGameKeyMaps
{
	forward = PG_KEY1,
	backward = PG_KEY3,
};

This way you will not have to memorize which PG_KEY triggers which action in your game.


Getting input from the keyboard
-------------------------------
After yo have mapped some keys you can check for them with IsKeyPressed(). This method take a PG_KEY argument and checks it against the internal
button state with a bitwise AND. Note that this will return true as soon and as long as a button is pressed, which makes it unsuitable for text input.
NOTE: Using RegisterListener() is currently deprecheated because events are sent right when they appear, meaning you can only check for one key per frame.
For example: Using RegisterListener() you cannot check if PG_KEY1 and PG_KEY2 are pressed simultaniously, as you receive either PG_KEY1 _or_ PG_KEY2 but
not both. To achieve such a behaviour, use IsKeyPressed().

*/
class PALEGEM_API pgInputEventManager : public IEventManager
{
public:

	pgInputEventManager();
	~pgInputEventManager();

	void ShutDown();

	/** Not used */
	void Update(float frameTime);

	/** Register a listener to receive input events DEPRECHEATED */
	void RegisterListener(IEventListener* listener, EVENT_CLASS eventClass = EC_UNKNOWN, EVENT_NAME eventName = E_UNKNOWN );

	/** Unregister a listener */
	void UnregisterListener(IEventListener* listener);

	/** 
	Register a keyboard input event.
	@param eventName Usually the wParam of the window procedure, but more precisely the windows virtual key number
	@param eventClass Pressed or released? See EKeyPressEventClass.
	@param pData Not used. Use NULL.
	*/
	void RegisterEvent(int eventName, int eventClass, IEventData *pData);

	/** 
	Check whether the specified key is pressed
	@param id See EKeyPressKeyEventName.
	@returns true if key is pressed.
	*/
	bool IsKeyPressed(unsigned int id);

	/**
	Checks if a key is pressed and sets it to released.
	@param id PG_KEY to check for
	@returns true if key is pressed.
	*/
	bool IsKeyTapped(unsigned int id);


	/**
	Map a windows key to a palegem key.
	@param virtualKey Windows key code of the key to be mapped.
	@param pgKey The PG_KEY the specified windows key shall be mapped to.
	*/
	void MapKey(BYTE virtualKey, unsigned int pgKey);

private:

	/** Sets a key's state to pressed. */
	void KeyPressed(unsigned int id);
	/** Sets a key's state to released */
	void KeyReleased(unsigned int id);

	/** Send events to listeners */
	void DispatchEvents(unsigned int pgKey, int activationMode);

	/** 1 is pressed 0 means released */
	unsigned int m_nButtonStates;

	/** Connects virtual keys to internal keynumbers */
	std::map<BYTE, unsigned int> m_keyMaps;
	/** List of objects that listen for input events */
	std::list<IEventListener*> m_listeners;
};